package com.horse.cloud.framework.redis;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.MapPropertySource;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

import java.io.Serializable;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * @create : Created by intelliJ IDEA 17.2
 * @author : bing.Pan
 * @e-mail : 15923508369@163.com
 * @gmdate : 11 02 2017 14:42
 * @sidesc :
 */

@Configuration
public class RedisGeneralConfig extends CachingConfigurerSupport{

    private static final Logger LOGGER = LoggerFactory.getLogger(RedisGeneralConfig.class);

    @Autowired private RedisProperties redisProperties;

    @Bean
    @ConditionalOnMissingBean
    public RedisProperties redisProperties(){ return new RedisProperties(); }

    @Bean
    @ConditionalOnMissingBean
    public JedisPoolConfig jedisPoolConfig(){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(redisProperties.getPool().getMaxTotal());
        jedisPoolConfig.setMaxWaitMillis(redisProperties.getPool().getMaxWait());
        jedisPoolConfig.setMaxIdle(redisProperties.getPool().getMaxIdle());
        jedisPoolConfig.setMinIdle(redisProperties.getPool().getMinIdle());

        jedisPoolConfig.setTestOnBorrow(redisProperties.getPool().getTestOnBorrow());
        jedisPoolConfig.setTestOnReturn(redisProperties.getPool().getTestOnReturn());
        jedisPoolConfig.setTestWhileIdle(redisProperties.getPool().getTestWhileIdle());
        jedisPoolConfig.setTimeBetweenEvictionRunsMillis(redisProperties.getPool().getTimeBetweenEvictionRunsMillis());
        jedisPoolConfig.setNumTestsPerEvictionRun(redisProperties.getPool().getNumTestsPerEvictionRun());
        jedisPoolConfig.setMinEvictableIdleTimeMillis(redisProperties.getPool().getMinEvictableIdleTimeMillis());

        return jedisPoolConfig;
    }

    @Bean
    @ConditionalOnMissingBean
    public JedisConnectionFactory jedisConnectionFactory(){
        String redisMode = redisProperties.getRedisMode();

        JedisClientConfiguration.JedisClientConfigurationBuilder jedisClientConfiguration = JedisClientConfiguration.builder();
        jedisClientConfiguration.connectTimeout(Duration.ofMillis(redisProperties.getTimeout()));

        JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpb=
                (JedisClientConfiguration.JedisPoolingClientConfigurationBuilder)JedisClientConfiguration.builder();

        jpb.poolConfig(jedisPoolConfig());


        LOGGER.debug("【framework-redis】读取配置文件属性[spring.redis.redisMode]的值为 [{}]",redisMode);
        if(StringUtils.isEmpty(redisMode) || redisMode.equalsIgnoreCase(RedisModeEnum.STAND_ALONE.getMode())){

            RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
            redisStandaloneConfiguration.setHostName( redisProperties.getStandAlone().getHost());
            redisStandaloneConfiguration.setPort(redisProperties.getStandAlone().getPort());
            redisStandaloneConfiguration.setDatabase(redisProperties.getStandAlone().getDatabase());
            redisStandaloneConfiguration.setPassword(RedisPassword.of(redisProperties.getStandAlone().getPassword()));

            return new JedisConnectionFactory(redisStandaloneConfiguration, jpb.build());

        }if(redisMode.equalsIgnoreCase(RedisModeEnum.CLUSTER.getMode())){

            Map<String, Object> source = new HashMap<>(16);
            source.put("spring.redis.cluster.nodes", redisProperties.getCluster().getNodes());
            source.put("spring.redis.cluster.max-redirects", redisProperties.getCluster().getMaxRedirects());
            RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(new MapPropertySource("RedisClusterConfiguration", source));

            return new JedisConnectionFactory(redisClusterConfiguration, jpb.build());

        }if(redisMode.equalsIgnoreCase(RedisModeEnum.SENTINEL.getMode())){
            Map<String, Object> source = new HashMap<>(16);
            source.put("spring.redis.sentinel.master", redisProperties.getCluster().getNodes());
            source.put("spring.redis.sentinel.nodes", redisProperties.getCluster().getMaxRedirects());

            RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration(new MapPropertySource("RedisSentinelConfiguration", source));

            return new JedisConnectionFactory(redisSentinelConfiguration, jpb.build());
        }else {
            LOGGER.debug("【framework-redis】读取配置文件属性[spring.redis.redisMode] 的值未在定义的类型中,不构建redis连接工厂");
            return  null;
        }

    }

    /**
     * 业务redisTemplate
     * @return
     */
    @Bean
    public RedisTemplate<Serializable, Object> redisTemplate() {
       return initRedisTemplate();
    }


    /**
     * 分布式锁独占redisTemplate
     * @return
     */
    @Bean
    public RedisTemplate<Serializable, Object> redisReentrantLockTemplate() {
        return initRedisTemplate();
    }


    private RedisTemplate<Serializable, Object> initRedisTemplate(){
        RedisTemplate<Serializable, Object> template = new RedisTemplate<Serializable, Object>();
        template.setConnectionFactory(jedisConnectionFactory());
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        RedisSerializer stringSerializer = new StringRedisSerializer();

        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();

        template.setKeySerializer(stringSerializer);
        template.setHashKeySerializer(stringSerializer);
        template.setHashValueSerializer(stringSerializer);

        return template;
    }



}
